#include "sdl_texdata.h"
#include "sdl_fliprgbasurface.h"
#include <cmath>

/*
 * class sdl_base_texdata;
 */

void sdl_base_texdata::load()
{
}

sdl_base_texdata::sdl_base_texdata(const std::string & n) : sdl_prim(n)
{
    inram = invideo = false;
}

sdl_base_texdata::~sdl_base_texdata()
{
}

/*
 * class sdl_texdata
 */
sdl_texdata::sdl_texdata(const std::string & _file, texture_type t, const std::string & n) :
sdl_base_texdata(n + _file),
file(give<sdl_resource_loader>()->load(_file)),
data(NULL),
textype(t)
{
    _msg("texdata", 6, "ładowanie tekstury " << name());
    ram_load();
}

sdl_texdata::~sdl_texdata()
{
    _msg("texdata", 5, "kasowanie tekstury " << name());
    strong_unload();
}

void sdl_texdata::sub_load()
{

    glEnable(GL_TEXTURE_2D);
    // generujemy teksturę
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    switch (textype) {

    case direct:
    {
        if (data->format->BytesPerPixel == 4)
            glTexImage2D(GL_TEXTURE_2D, 0, 4, data->w, data->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data->pixels);
        else
            glTexImage2D(GL_TEXTURE_2D, 0, 3, data->w, data->h, 0, GL_RGB, GL_UNSIGNED_BYTE, data->pixels);

        break;
    }

    case mipmap:
    {
        // domyślnie ładujemy jako mipmapy (zmienić?)
        if (data->format->BytesPerPixel == 4)
            gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, data->w, data->h, GL_RGBA, GL_UNSIGNED_BYTE, data->pixels);
        else
            gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, data->w, data->h, GL_RGB, GL_UNSIGNED_BYTE, data->pixels);
    }
    }

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // If the u,v coordinates overflow the range 0,1 the image is repeated
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);


    /*
     * jeżeli rozszerzenie jest dostępne, włączamy filtrowanie anizotropowe
     */

    if (gl_extension_supported("GL_EXT_texture_filter_anisotropic")) {
        float laniso = 0.0;
        glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &laniso);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, laniso);
    }

    invideo = true;

    //	std::cout << "Loaded " << file << " into video memory" << std::endl;
}

void sdl_texdata::load()
{
    _msg("texdata", 6, "ładowanie do pamięci video tekstury " << name());
    ram_load();

    if (!invideo && inram) {
        sub_load();
        invideo = true;
    }

}

void sdl_texdata::unload()
{
    _msg("texdata", 6, "wyładowanie z pamięci video tekstury " << name());

    if (invideo)
        glDeleteTextures(1, &texture);

    invideo = false;
}

void sdl_texdata::strong_unload()
{
    _msg("texdata", 6, "całkowite wyładowanie tekstury " << name());

    if (inram) {
        SDL_FreeSurface(data);
        data = NULL;
        inram = false;
    }

    if (invideo)
        glDeleteTextures(1, &texture);

    invideo = false;
}

void sdl_texdata::ram_unload()
{
    if (inram) {
        _msg("texdata", 6, "wyładowanie z pamięci RAM tekstury " << name());
        SDL_FreeSurface(data);
        data = NULL;
        inram = false;
    }
}

void sdl_texdata::ram_load()
{
    if (!inram) {
        _msg("texdata", 6, "ładowanie z dysku tekstury " << name() << " z pliku " << file.get_path_str());
        // załadowanie pliku

        if (!boost::filesystem::exists(file.get_path())) {
            _err("texdata", "błąd ładowania pliku " << file.get_path_str().c_str());
        } else {
            data = OpenGL_convert(IMG_Load(file.get_path_str().c_str()));
            inram = true;
        }
    }
}

/*
 * struct sdl_mipmap_data
 */
sdl_mipmap_data::sdl_mipmap_data() :
data(NULL)
{
    level = max = 0;
}

sdl_mipmap_data::sdl_mipmap_data(const sdl_mipmap_data & s)
{
    data = s.data;
    level = s.level;
    max = s.max;
}

void sdl_mipmap_data::free()
{
    if (data != NULL) {
        SDL_FreeSurface(data);
        data = NULL;
    }
}

/*
 * class sdl_custom_mipmap_texdata
 *
 */

sdl_custom_mipmap_texdata::sdl_custom_mipmap_texdata(std::string d, const std::string & n) :
sdl_base_texdata(n + d),
dir(d),
base_res(512)
{
    sdl_resource mip_dir = give<sdl_resource_loader > ()->load(d);

    _msg("texdata", 6, "tworzenie mipmapy: " << name());
    fs::path cfgfile = (mip_dir.get_path() / "mipmap.cfg");
    _msg("texdata", 6, "otwieranie " << cfgfile);

    if (!boost::filesystem::exists(cfgfile))
        _err("texdata", "brak pliku konfiguracyjnego " << cfgfile);

    std::ifstream config(cfgfile.string().c_str());

    while (!config.eof()) {
        std::string buf;
        config >> buf;

        if (buf == std::string("base")) {
            config >> buf;
            base_res = std::atoi(buf.c_str());
        }

        if (buf == std::string("texture")) {
            sdl_mipmap_data mydata;
            config >> buf;
            mydata.level = std::atoi(buf.c_str());
            config >> buf;
            mydata.max = std::atoi(buf.c_str());
            config >> buf;
            fs::path curr_file = (mip_dir.get_path() / buf);

            if (!boost::filesystem::exists(curr_file)) {
                _err("texdata", "błąd odczytu pliku: " << curr_file);
            } else {
                mydata.data = OpenGL_convert(IMG_Load(curr_file.string().c_str()));
                _msg("texdata", 4, "wczytano " << curr_file);
            }

            data.push_back(mydata);
        }
    }

    inram = true;
}

sdl_custom_mipmap_texdata::~sdl_custom_mipmap_texdata()
{
    _msg("texdata", 5, "kasowanie mipmapy " << name());

    //tym zajmuje się destruktor wyżej
    if (invideo)
        glDeleteTextures(1, &texture);

    invideo = false;

    for (std::list<sdl_mipmap_data>::iterator i = data.begin(); i != data.end(); i++)
        i->free();
}

void sdl_custom_mipmap_texdata::load()
{
    if (!invideo && inram) {
        _msg("texdata", 5, "ładowanie tekstury do pamięci wideo: " << name());
        glEnable(GL_TEXTURE_2D);
        // generujemy teksturę
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);

        if (gl_extension_supported("GL_SGIS_generate_mipmap"))
            glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
        else
            glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);

        for (std::list<sdl_mipmap_data>::iterator i = data.begin(); i != data.end(); i++) {

            for (int j = i->level; j <= i->max; j++) {
                _msg("texdata", 2, "budowanie mipmapy dla poziomu " << j);
                int lf = std::pow(2.0f, j);
                int outx = base_res / lf;
                int outy = base_res / lf;
                _msg("texdata", 2, "reskalowanie " << i->data->w << "x" << i->data->h << " do " << outx << "x" << outy);

                GLubyte * resized = new GLubyte[4 * outx * outy];

                if (i->data->format->BytesPerPixel == 4) {
                    gluScaleImage(GL_RGBA, i->data->w, i->data->h, GL_UNSIGNED_BYTE, i->data->pixels, outx, outy, GL_UNSIGNED_BYTE, resized);
                    glTexImage2D(GL_TEXTURE_2D, j, GL_RGBA, outx, outy, 0, GL_RGBA, GL_UNSIGNED_BYTE, (void*) resized);
                } else {
                    gluScaleImage(GL_RGB, i->data->w, i->data->h, GL_UNSIGNED_BYTE, i->data->pixels, outx, outy, GL_UNSIGNED_BYTE, resized);
                    glTexImage2D(GL_TEXTURE_2D, j, GL_RGB, outx, outy, 0, GL_RGB, GL_UNSIGNED_BYTE, (void*) resized);
                }

                delete [] resized;
            }

        }
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // If the u,v coordinates overflow the range 0,1 the image is repeated
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        if (gl_extension_supported("GL_SGIS_generate_mipmap"))
            glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_FALSE);
        else
            glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);

        /*
         * jeżeli rozszerzenie jest dostępne, włączamy filtrowanie anizotropowe
         */
        if (gl_extension_supported("GL_EXT_texture_filter_anisotropic")) {
            float laniso = 0.0;
            glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &laniso);
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, laniso);
        }

        invideo = true;
    }
}
